use eds_reader::Reader;
use eds_writer::Writer;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use unmp_link::prelude::*;

const DATA_LEN: usize = 4096;

pub struct Conn {
    identifier: String,
    recv_buf: Vec<u8>,
    serial: tokio_serial::SerialStream,
}
impl Conn {
    /// 创建一个串口链路实例
    pub fn new(path: String, baud_rate: u32) -> Result<Connection, ()> {
        let serial = tokio_serial::new(&path, baud_rate);
        let serial = tokio_serial::SerialStream::open(&serial).map_err(|_| ())?;
        let identifier = path;
        Ok(Connection::new(Box::new(Self {
            identifier,
            recv_buf: Vec::new(),
            serial,
        })))
    }
}
#[async_trait]
impl ConnectionDriver for Conn {
    fn is_subnet(&self) -> bool {
        true
    }
    fn kind(&self) -> &str {
        "serial_server"
    }
    fn id(&self) -> &str {
        &self.identifier
    }
    async fn send(&mut self, buf: Bytes) -> Result<(), SendError> {
        let mut eds_writer = Writer::new(3);
        let mut tx = eds_writer.get_load().unwrap();
        tx.extend_from_slice(&buf);
        let data = eds_writer.get_data(tx);
        match self.serial.write_all(data).await {
            Ok(_) => Ok(()),
            Err(_) => Err(SendError::Disconnected),
        }
    }
    async fn recv(&mut self) -> Result<Bytes, RecvError> {
        let mut reader = Reader::new(2);
        if !self.recv_buf.is_empty() {
            let l = reader.recv(&self.recv_buf);
            self.recv_buf = Vec::from(&self.recv_buf[l..]);
            if let Some(load) = reader.get_load() {
                return Ok(load);
            }
        }

        let mut buf = [0u8; DATA_LEN];
        loop {
            let len = match self.serial.read(&mut buf).await {
                Ok(len) => len,
                Err(_) => return Err(RecvError::Disconnected),
            };
            let l = reader.recv(&buf[..len]);
            if let Some(load) = reader.get_load() {
                self.recv_buf = Vec::from(&buf[l..len]);
                return Ok(load);
            }
        }
    }
}
